#include "camera.h"

#include "common.h"

namespace Opl{ namespace Gui {

        class CameraPrivate{
        public:
            CameraPrivate()
                :yaw(0)
                , pitch(0)
                , fov(70)
                , time(0)
                , matrixDirty(true)
            {}

            qreal yaw;
            qreal pitch;
            qreal fov;
            qreal time;

            QPointF pos;

            mutable bool matrixDirty;
            mutable QMatrix4x4 viewMatrix;
            mutable QMatrix4x4 viewProjectionMatrix;
        };

    Camera::Camera()
     :O_PRIVATE_CREATE(Camera)
            {
            }

    Camera::~Camera()
    {
        O_PRIVATE_DELETE;
    }

    qreal Camera::yaw() const
    {
        O_D(Camera);
        return d->yaw;

    }
    qreal Camera::pitch() const
    {
        O_D(Camera);
        return d->pitch;
    }

    qreal Camera::fov() const
    {
        O_D(Camera);
        return d->fov;
    }

    QPointF Camera::pos() const
    {
        O_D(Camera);
        return d->pos;
    }

    void Camera::setPitch(qreal pitch)
    {
        O_D(Camera);
        d->pitch = qBound(qreal(-30), pitch, qreal(30));
        d->matrixDirty = true;
    }

    void Camera::setYaw(qreal yaw)
    {
        O_D(Camera);
        d->yaw = yaw;
        d->matrixDirty = true;
    }

    void Camera::setPos(const QPointF &pos)
    {
        O_D(Camera);
        d->pos = pos;
        d->matrixDirty = true;
    }

    void Camera::setFov(qreal fov)
    {
        O_D(Camera);
        d->fov = fov;
        d->matrixDirty = true;
    }

    void Camera::setTime(qreal time)
    {
        O_D(Camera);
        d->time = time;
        d->matrixDirty = true;
    }


    const QMatrix4x4 &Camera::viewMatrix() const
    {
        O_D(Camera);
        updateMatrix();
        return d->viewMatrix;
    }

    const QMatrix4x4 &Camera::viewProjectionMatrix() const
    {
        O_D(Camera);
        updateMatrix();
        return d->viewProjectionMatrix;
    }

    void Camera::updateMatrix() const
    {
        O_D(Camera);
        if (!d->matrixDirty)
            return;

        d->matrixDirty = false;

        QMatrix4x4 m;
        m.scale(-1, 1, 1);
        m *=  Opl::Gui::Common::fromRotation(d->yaw + 180, Qt::YAxis);
        m.translate(-d->pos.x(), 0.04 * qSin(10 * d->time) + 0.1, -d->pos.y());
        m = Opl::Gui::Common::fromRotation(d->pitch, Qt::XAxis) * m;
        d->viewMatrix = m;
        d->viewProjectionMatrix = Opl::Gui::Common::fromProjection(d->fov) * d->viewMatrix;
    }

}; };
